Developing a Stratified Sampling Scheme for the Unmonitored
Systems Using K-means Clustering Method
Background
The aim of this analysis is to analyze SMP population using CWL
monitoring data, categorize it, and identify underrepresented SMP
features in our data set. We will also use clustering to group systems
with similar characteristics and develop a stratified sampling script to
randomly select a representative sample of systems proportional to their
cluster and system type. This will ensure a comprehensive data set
covering all types of SMPs with varying design metrics such as loading
ratios, drainage area, and storm size management. The output of this
analysis will help the data collection team with their future site
selection.
- Finding the over-represented/under-represented
SMPs
#Libraries
library(odbc)
library(DBI)
library(lubridate)
library(tidyverse)
library(stats)
library(gridExtra)
library(grid)
library(gtable)
library(ggtext)
library(dplyr)
library(ggplot2)
library(knitr)
library(plotly)
library(cluster)
library(factoextra)
# DB PG14
con <- dbConnect(odbc::odbc(), dsn = "mars14_data", uid = Sys.getenv("shiny_uid"), pwd = Sys.getenv("shiny_pwd"), MaxLongVarcharSize = 8190)
#Getting list of SMPs
cwl_smp <- dbGetQuery(con,"SELECT DISTINCT smp_id
FROM fieldwork.viw_deployment_full_cwl")
#Getting the greenit info
smpbdv <- dbGetQuery(con,"SELECT * FROM external.tbl_smpbdv")
sysbdv <- dbGetQuery(con,"SELECT * FROM external.tbl_systembdv")
greenit_unified <-dbGetQuery(con,"SELECT * FROM external.viw_greenit_unified")
# join the greenit table with our smp list
smp_features <- cwl_smp %>%
inner_join(smpbdv, by="smp_id")
# Unmonitored SMPs
unmonitored_smp <- read.csv("C:\\Users\\Farshad.Ebrahimi\\OneDrive - City of Philadelphia\\Github Projects\\smp-population-assesment\\unmonitored_sites.csv") %>%
select(smp_id = SMP.ID) %>%
distinct()
# unmonitored smp_features
unmonitored_smp_features <- unmonitored_smp %>%
inner_join(smpbdv, by="smp_id")
The code calculates the distribution of SMP types with CWL data in
the MARS database, providing an initial understanding of the
distribution of SMP subtypes.
#number of smp types
smp_type_break <- smp_features %>%
group_by(smp_smptype) %>%
summarise(count = n()) %>%
select(Type = smp_smptype, count)

#number of unmonitored smps
unmonitored_smp_type_break <- unmonitored_smp_features %>%
group_by(smp_smptype) %>%
summarise(count = n()) %>%
select(Type = smp_smptype, count)
Similar analysis is performed below to calculate the distribution of
SMP types in the entire population of SMP including the monitored and
unmonitored SMPs.

The analysis aims to determine the over-represented (normalized
percent > 1) and under-represented (normalized percent < 1) SMP
types by dividing the fraction of a specific SMP type in the monitored
data by the fraction in the entire population.

#combining all smps (monitored and unmonitored) and get fractions based on type of smp-finally normalize them
total_smp <- unmonitored_smp_type_break %>%
full_join(smp_type_break, by="Type")
total_smp[is.na(total_smp)] <- 0
total_smp <- total_smp %>%
mutate(total_number = count.x+count.y) %>%
select(Type, total_number)
total_smp <- total_smp %>%
mutate(sum_all = sum(total_number)) %>%
mutate(percent_all = (total_number/sum_all)*100)
#get the percent of monitored SMP
smp_type_break_fraction <- smp_type_break %>%
mutate(sum_all = sum(count)) %>%
mutate(percent_monitored = (count/sum_all)*100)
#normalize the monitored smp by the total smp percents
normalized_fractions <- smp_type_break_fraction %>%
full_join(total_smp, by="Type") %>%
mutate(normalized_percent = percent_monitored/percent_all) %>%
select(Type, percent_monitored, percent_all, normalized_percent)
normalized_fractions[is.na(normalized_fractions)] <- 0
#calculating the normalized percentage of smp types
kable(arrange(normalized_fractions, normalized_percent), caption = "Normalized SMP Break-down")
Normalized SMP Break-down
| Green Roof |
0.0000000 |
0.1490313 |
0.0000000 |
| Stormwater Tree |
0.0000000 |
4.1728763 |
0.0000000 |
| Bumpout |
2.8017241 |
8.5692996 |
0.3269490 |
| Swale |
1.2931034 |
2.5335320 |
0.5103955 |
| Planter |
5.8189655 |
7.5260805 |
0.7731734 |
| Infiltration/Storage Trench |
25.8620690 |
29.4336811 |
0.8786556 |
| Basin |
0.2155172 |
0.2235469 |
0.9640805 |
| Rain Garden |
12.0689655 |
11.1028316 |
1.0870169 |
| Tree Trench |
50.6465517 |
35.6929955 |
1.4189493 |
| Pervious Paving |
0.4310345 |
0.2980626 |
1.4461207 |
| Drainage Well |
0.8620690 |
0.2980626 |
2.8922414 |
The table above shows Tree trenches are over-represented in our CWL
data, while bumpout, swale and planters are under-represented (due to
monitoring limitation, etc). However, it should be noted that MARS does
not plan on monitoring greef roofs due to limitations on access to these
systems and a 1:1 loading ratios. Stromwater trees, smilarly, does not
have any CWL data and the monitoring efforts are limited to CET tests
only. Porous pavements are also limited to infiltration rate testing and
have no CWL data.
- Clustering at the system-level
The current section aims to categorize the systems within each broad
subtypes. The reason to perform this at the system level is the
accessibility of design metrics at the system level. The clustering
within each type of systems is done to ensure the sampling approach
picks an appropriate number of systems from systems possessing similar
characteristics within these types. For example, the sampling method
should select a larger number of Bioinfiltration systems with higher
drainage area if those systems constitute the majority of
Bioinfiltration systems.
The binning approach considers several system features like loading
ratios, drainage area, and storm size management. The code chunks below
demonstrate a clustering method applied to unmonitored systems,
regardless of subtypes, as an example of how it can group systems based
solely on these three design metrics. The clustering will then be
repeated within each subtype to provide a more relevant method for
sampling.
#rawstormsizemanaged-in , sys_impervda_ft2, and loading ratio metrics gathering
table_all <- unmonitored_smp_features %>%
left_join(sysbdv, by="system_id") %>%
mutate(loading_ratio = sys_lrtotalda_ft2 )
K-means clustering is an unsupervised learning method that groups
data points based on their proximity to centroids, which are calculated
as the mean of all points in a cluster. The algorithm starts by randomly
selecting initial centroids, and then repeatedly adjusts them until
convergence is achieved
The optimal number of clusters is determined using the Elbow method.
In summary, this method picks the number of clusters in which a the
slope of the lines changes more drastically (cluster 4 in the following
graph).
#Clustring analysis based on 3 features of drainage area, loading ratio, and storm sized managed
clustered_moniored <- table_all %>%
select(system_id, sys_impervda_ft2, loading_ratio, sys_rawstormsizemanaged_in) %>%
na.omit() %>%
distinct()
#normalze using mean-sd standardization
normalized_cluster <- clustered_moniored
normalized_cluster[,2:4] <- scale(clustered_moniored[,2:4])
#Elbow method shows a subtle elbow at cluster 4-hence 4 clusters is optimum
#number of clusters
fviz_nbclust(normalized_cluster[,2:4], kmeans, method = "wss")

#k-means
# Cluster using kmeans with five clusters
cluster_solution <- kmeans(normalized_cluster[,2:4], centers = 4)
# Store the cluster assignments back into the clustering data frame object
clustered_moniored$cluster <-factor(cluster_solution$cluster)
# Look at the distribution of cluster assignments
table(clustered_moniored$cluster)
1 2 3 4
21 279 136 11
The above table displays the number of systems in each cluster. The
subsequent tables aim to determine the average of system design metrics
in each category. The data can be analyzed by examining each category;
for instance, Cluster Four encompasses systems with a large drainage
area, capable of handling larger storm sizes (>3 inch/hr).
# Group by the cluster assignment and calculate averages
sys_clus_avg <- clustered_moniored %>%
group_by(cluster) %>%
summarize_if(is.numeric, mean)
# View the resulting table
kable(sys_clus_avg)
| 1 |
17695.05 |
68.36332 |
1.491118 |
| 2 |
20363.52 |
12.75067 |
1.886491 |
| 3 |
17350.55 |
23.52892 |
1.212609 |
| 4 |
62204.82 |
10.56821 |
3.843215 |
ggplot(data = clustered_moniored, aes(x = loading_ratio, y = sys_rawstormsizemanaged_in, color = cluster)) +
geom_point()

plot_ly(clustered_moniored, x = ~sys_impervda_ft2, y = ~loading_ratio, z = ~sys_rawstormsizemanaged_in) %>%
add_markers(color = ~cluster)
In order to create a more pertinent clustering method that
distinguishes between general types of systems (lined vs. unlined or
bioretention vs. bioinfiltration), the unmonitored systems were divided
into broad categories, including Bioinfiltration, Bioretention (lined),
Bioretention (unlined), Subsurface Infiltration, Subsurface Slow Release
(lined), and Subsurface Slow Release (unlined) using the system-level
feature called sys_modelinputcategory. The code chunk below displays a
breakdown of these SMPs. The clustering algorithm was repeated for each
of those groups similarly. Please note that Green Roofs and Permeable
Pavements were not included due to a low number of systems.
#sys_modelinputcategory categories
sys_cat <- table_all %>%
select(system_id, sys_modelinputcategory)%>%
distinct %>%
group_by(sys_modelinputcategory) %>%
summarise(count = n())
kable(sys_cat)
| Bioinfiltration |
124 |
| Bioretention (lined) |
29 |
| Bioretention (unlined) |
83 |
| Green Roof |
2 |
| Permeable Pavement |
1 |
| Subsurface infiltration |
159 |
| Subsurface slow release (lined) |
113 |
| Subsurface slow release (unlined) |
113 |
-Bioinfiltration Clustering:
clustered_Bioinfiltration <- table_all %>%
filter(sys_modelinputcategory == "Bioinfiltration") %>%
select(system_id, sys_impervda_ft2, loading_ratio, sys_rawstormsizemanaged_in, sys_modelinputcategory) %>%
na.omit() %>%
distinct()
#normalze using mean-sd standardization
normalized_cluster <- clustered_Bioinfiltration
normalized_cluster[,2:4] <- scale(clustered_Bioinfiltration[,2:4])
#Elbow method shows a subtle elbow at cluster 4-hence 4 clusters is optimum
#number of clusters
fviz_nbclust(normalized_cluster[,2:4], kmeans, method = "wss")

#k-means
# Cluster using kmeans with five clusters
cluster_solution <- kmeans(normalized_cluster[,2:4], centers = 4)
# Store the cluster assignments back into the clustering data frame object
clustered_Bioinfiltration$cluster <-factor(cluster_solution$cluster)
# Look at the distribution of cluster assignments
table(clustered_Bioinfiltration$cluster)
1 2 3 4
17 31 72 4
# Group by the cluster assignment and calculate averages
sys_clus_avg <- clustered_Bioinfiltration %>%
group_by(cluster) %>%
summarize_if(is.numeric, mean)
# View the resulting table
kable(sys_clus_avg)
| 1 |
11184.24 |
40.58019 |
1.063568 |
| 2 |
51515.00 |
15.79839 |
1.752800 |
| 3 |
15718.52 |
12.39893 |
1.778769 |
| 4 |
22358.50 |
10.18006 |
4.348037 |
#3 D plot
plot_ly(clustered_Bioinfiltration, x = ~sys_impervda_ft2, y = ~loading_ratio, z = ~sys_rawstormsizemanaged_in) %>%
add_markers(color = ~cluster)
-Bioretention (lined) clustering. Note that loading ratio
field is not applicable here:
clustered_Bioreten_lined <- table_all %>%
filter(sys_modelinputcategory == "Bioretention (lined)") %>%
select(system_id, sys_impervda_ft2, sys_rawstormsizemanaged_in, sys_modelinputcategory) %>%
na.omit() %>%
distinct()
#normalze using mean-sd standardization
normalized_cluster <- clustered_Bioreten_lined
normalized_cluster[,2:3] <- scale(clustered_Bioreten_lined[,2:3])
#Elbow method shows a subtle elbow at cluster 4-hence 4 clusters is optimum
#number of clusters
fviz_nbclust(normalized_cluster[,2:3], kmeans, method = "wss")

#k-means
# Cluster using kmeans with five clusters
cluster_solution <- kmeans(normalized_cluster[,2:3], centers = 2)
# Store the cluster assignments back into the clustering data frame object
clustered_Bioreten_lined$cluster <-factor(cluster_solution$cluster)
# Look at the distribution of cluster assignments
table(clustered_Bioreten_lined$cluster)
1 2
5 24
# Group by the cluster assignment and calculate averages
sys_clus_avg <- clustered_Bioreten_lined %>%
group_by(cluster) %>%
summarize_if(is.numeric, mean)
# View the resulting table
kable(sys_clus_avg)
| 1 |
73919.40 |
2.270016 |
| 2 |
18291.83 |
1.473926 |
#3 D plot
plot_ly(clustered_Bioreten_lined, x = ~sys_impervda_ft2, y = ~sys_rawstormsizemanaged_in) %>%
add_markers(color = ~cluster)
Warning in RColorBrewer::brewer.pal(N, "Set2") :
minimal value for n is 3, returning requested palette with 3 different levels
Warning in RColorBrewer::brewer.pal(N, "Set2") :
minimal value for n is 3, returning requested palette with 3 different levels
Warning in RColorBrewer::brewer.pal(N, "Set2") :
minimal value for n is 3, returning requested palette with 3 different levels
Warning in RColorBrewer::brewer.pal(N, "Set2") :
minimal value for n is 3, returning requested palette with 3 different levels
-Bioretention (unlined) clustering:
clustered_Bioreten_unlined <- table_all %>%
filter(sys_modelinputcategory == "Bioretention (unlined)") %>%
select(system_id, sys_impervda_ft2, sys_rawstormsizemanaged_in, loading_ratio, sys_modelinputcategory) %>%
na.omit() %>%
distinct()
#normalze using mean-sd standardization
normalized_cluster <- clustered_Bioreten_unlined
normalized_cluster[,2:4] <- scale(clustered_Bioreten_unlined[,2:4])
#Elbow method shows a subtle elbow at cluster 4-hence 4 clusters is optimum
#number of clusters
fviz_nbclust(normalized_cluster[,2:4], kmeans, method = "wss")

#k-means
# Cluster using kmeans with five clusters
cluster_solution <- kmeans(normalized_cluster[,2:4], centers = 3)
# Store the cluster assignments back into the clustering data frame object
clustered_Bioreten_unlined$cluster <-factor(cluster_solution$cluster)
# Look at the distribution of cluster assignments
table(clustered_Bioreten_unlined$cluster)
1 2 3
9 21 53
# Group by the cluster assignment and calculate averages
sys_clus_avg <- clustered_Bioreten_unlined %>%
group_by(cluster) %>%
summarize_if(is.numeric, mean)
# View the resulting table
kable(sys_clus_avg)
| 1 |
7046.333 |
3.309078 |
4.263439 |
| 2 |
42812.095 |
1.525561 |
20.624402 |
| 3 |
16789.575 |
1.639162 |
12.959101 |
#3 D plot
plot_ly(clustered_Bioreten_unlined, x = ~sys_impervda_ft2, y = ~sys_rawstormsizemanaged_in, z= ~loading_ratio) %>%
add_markers(color = ~cluster)
-Subsurface infiltration clustering:
clustered_subsurface <- table_all %>%
filter(sys_modelinputcategory == "Subsurface infiltration") %>%
select(system_id, sys_impervda_ft2, sys_rawstormsizemanaged_in, loading_ratio, sys_modelinputcategory) %>%
na.omit() %>%
distinct()
#normalze using mean-sd standardization
normalized_cluster <- clustered_subsurface
normalized_cluster[,2:4] <- scale(clustered_subsurface[,2:4])
#Elbow method shows a subtle elbow at cluster 4-hence 4 clusters is optimum
#number of clusters
fviz_nbclust(normalized_cluster[,2:4], kmeans, method = "wss")

#k-means
# Cluster using kmeans with five clusters
cluster_solution <- kmeans(normalized_cluster[,2:4], centers = 2)
# Store the cluster assignments back into the clustering data frame object
clustered_subsurface$cluster <-factor(cluster_solution$cluster)
# Look at the distribution of cluster assignments
table(clustered_subsurface$cluster)
1 2
99 26
# Group by the cluster assignment and calculate averages
sys_clus_avg <- clustered_subsurface %>%
group_by(cluster) %>%
summarize_if(is.numeric, mean)
# View the resulting table
kable(sys_clus_avg)
| 1 |
19525.382 |
1.837962 |
12.69806 |
| 2 |
7850.692 |
1.030724 |
31.54683 |
#3 D plot
plot_ly(clustered_subsurface, x = ~sys_impervda_ft2, y = ~sys_rawstormsizemanaged_in, z= ~loading_ratio) %>%
add_markers(color = ~cluster)
Warning in RColorBrewer::brewer.pal(N, "Set2") :
minimal value for n is 3, returning requested palette with 3 different levels
Warning in RColorBrewer::brewer.pal(N, "Set2") :
minimal value for n is 3, returning requested palette with 3 different levels
Warning in RColorBrewer::brewer.pal(N, "Set2") :
minimal value for n is 3, returning requested palette with 3 different levels
Warning in RColorBrewer::brewer.pal(N, "Set2") :
minimal value for n is 3, returning requested palette with 3 different levels
-Subsurface slow release (lined) clustering:
clustered_slowrel_lined <- table_all %>%
filter(sys_modelinputcategory == "Subsurface slow release (lined)") %>%
select(system_id, sys_impervda_ft2, sys_rawstormsizemanaged_in, sys_modelinputcategory) %>%
na.omit() %>%
distinct()
#normalze using mean-sd standardization
normalized_cluster <- clustered_slowrel_lined
normalized_cluster[,2:3] <- scale(clustered_slowrel_lined[,2:3])
#Elbow method shows a subtle elbow at cluster 4-hence 4 clusters is optimum
#number of clusters
fviz_nbclust(normalized_cluster[,2:3], kmeans, method = "wss")

#k-means
# Cluster using kmeans with five clusters
cluster_solution <- kmeans(normalized_cluster[,2:3], centers = 2)
# Store the cluster assignments back into the clustering data frame object
clustered_slowrel_lined$cluster <-factor(cluster_solution$cluster)
# Look at the distribution of cluster assignments
table(clustered_slowrel_lined$cluster)
1 2
58 55
# Group by the cluster assignment and calculate averages
sys_clus_avg <- clustered_slowrel_lined %>%
group_by(cluster) %>%
summarize_if(is.numeric, mean)
# View the resulting table
kable(sys_clus_avg)
| 1 |
13139.92 |
1.195020 |
| 2 |
20312.98 |
1.720648 |
#3 D plot
plot_ly(clustered_slowrel_lined, x = ~sys_impervda_ft2, y = ~sys_rawstormsizemanaged_in) %>%
add_markers(color = ~cluster)
Warning in RColorBrewer::brewer.pal(N, "Set2") :
minimal value for n is 3, returning requested palette with 3 different levels
Warning in RColorBrewer::brewer.pal(N, "Set2") :
minimal value for n is 3, returning requested palette with 3 different levels
Warning in RColorBrewer::brewer.pal(N, "Set2") :
minimal value for n is 3, returning requested palette with 3 different levels
Warning in RColorBrewer::brewer.pal(N, "Set2") :
minimal value for n is 3, returning requested palette with 3 different levels
-Subsurface slow release (unlined) clustering:
clustered_slowrel_unlined <- table_all %>%
filter(sys_modelinputcategory == "Subsurface slow release (unlined)") %>%
select(system_id, sys_impervda_ft2, sys_rawstormsizemanaged_in, loading_ratio, sys_modelinputcategory) %>%
na.omit() %>%
distinct()
#normalze using mean-sd standardization
normalized_cluster <- clustered_slowrel_unlined
normalized_cluster[,2:3] <- scale(clustered_slowrel_unlined[,2:3])
#Elbow method shows a subtle elbow at cluster 4-hence 4 clusters is optimum
#number of clusters
fviz_nbclust(normalized_cluster[,2:3], kmeans, method = "wss")

#k-means
# Cluster using kmeans with five clusters
cluster_solution <- kmeans(normalized_cluster[,2:3], centers = 2)
# Store the cluster assignments back into the clustering data frame object
clustered_slowrel_unlined$cluster <-factor(cluster_solution$cluster)
# Look at the distribution of cluster assignments
table(clustered_slowrel_unlined$cluster)
1 2
61 52
# Group by the cluster assignment and calculate averages
sys_clus_avg <- clustered_slowrel_unlined %>%
group_by(cluster) %>%
summarize_if(is.numeric, mean)
# View the resulting table
kable(sys_clus_avg)
| 1 |
18886.52 |
1.910538 |
19.26955 |
| 2 |
16819.60 |
1.337858 |
34.30980 |
#3 D plot
plot_ly(clustered_slowrel_unlined, x = ~sys_impervda_ft2, y = ~sys_rawstormsizemanaged_in, z = ~loading_ratio) %>%
add_markers(color = ~cluster)
Warning in RColorBrewer::brewer.pal(N, "Set2") :
minimal value for n is 3, returning requested palette with 3 different levels
Warning in RColorBrewer::brewer.pal(N, "Set2") :
minimal value for n is 3, returning requested palette with 3 different levels
Warning in RColorBrewer::brewer.pal(N, "Set2") :
minimal value for n is 3, returning requested palette with 3 different levels
Warning in RColorBrewer::brewer.pal(N, "Set2") :
minimal value for n is 3, returning requested palette with 3 different levels
Upon clustering the systems, the data will be collected in a unified
data frame for stratified sampling. The stratified sampling creates a
balanced list of systems that are reflective of the size of each system
type and that of the internal clusters. For example, the code below has
generated 58 system (~ 10% of total unmonitored systems); the size of
each system type is proportional to that of the population, and within
each type of systems, a proportional number of systems has been chosen
from each cluster e.g., in Bioinfiltration type systems, 7 systems are
randomly chosen from cluster 3, 3 systems from cluster 2, and 2 systems
from cluster 1.
#binding all sub-type groups into one table
sys_clustered_all <- bind_rows(clustered_Bioinfiltration[,c("system_id","cluster","sys_modelinputcategory")], clustered_Bioreten_lined[,c("system_id","cluster","sys_modelinputcategory")], clustered_Bioreten_unlined[,c("system_id","cluster","sys_modelinputcategory")], clustered_slowrel_lined[,c("system_id","cluster","sys_modelinputcategory")], clustered_slowrel_unlined[,c("system_id","cluster","sys_modelinputcategory")], clustered_subsurface[,c("system_id","cluster","sys_modelinputcategory")])
#sampling code
stratified_sample <- sys_clustered_all %>%
group_by(sys_modelinputcategory, cluster) %>%
sample_frac(size=0.1)
kable(stratified_sample)
| 1023-11 |
1 |
Bioinfiltration |
| 443-1 |
1 |
Bioinfiltration |
| 1083-6 |
2 |
Bioinfiltration |
| 1127-1 |
2 |
Bioinfiltration |
| 1195-3 |
2 |
Bioinfiltration |
| 991-1 |
3 |
Bioinfiltration |
| 310-6 |
3 |
Bioinfiltration |
| 410-2 |
3 |
Bioinfiltration |
| 1315-1 |
3 |
Bioinfiltration |
| 1140-2 |
3 |
Bioinfiltration |
| 244-1 |
3 |
Bioinfiltration |
| 416-1 |
3 |
Bioinfiltration |
| 1007-2 |
2 |
Bioretention (lined) |
| 1067-4 |
2 |
Bioretention (lined) |
| 1178-6 |
1 |
Bioretention (unlined) |
| 1273-6 |
2 |
Bioretention (unlined) |
| 578-2 |
2 |
Bioretention (unlined) |
| 595-4 |
3 |
Bioretention (unlined) |
| 596-4 |
3 |
Bioretention (unlined) |
| 1265-3 |
3 |
Bioretention (unlined) |
| 1200-3 |
3 |
Bioretention (unlined) |
| 588-2 |
3 |
Bioretention (unlined) |
| 443-3 |
1 |
Subsurface infiltration |
| 1051-15 |
1 |
Subsurface infiltration |
| 280-3 |
1 |
Subsurface infiltration |
| 344-1 |
1 |
Subsurface infiltration |
| 1070-6 |
1 |
Subsurface infiltration |
| 1281-27 |
1 |
Subsurface infiltration |
| 637-1 |
1 |
Subsurface infiltration |
| 1010-5 |
1 |
Subsurface infiltration |
| 1242-1 |
1 |
Subsurface infiltration |
| 1138-1 |
1 |
Subsurface infiltration |
| 441-13 |
2 |
Subsurface infiltration |
| 1298-1 |
2 |
Subsurface infiltration |
| 441-12 |
2 |
Subsurface infiltration |
| 1059-3 |
1 |
Subsurface slow release (lined) |
| 1240-2 |
1 |
Subsurface slow release (lined) |
| 1202-2 |
1 |
Subsurface slow release (lined) |
| 525-1 |
1 |
Subsurface slow release (lined) |
| 554-7 |
1 |
Subsurface slow release (lined) |
| 994-3 |
1 |
Subsurface slow release (lined) |
| 634-1 |
2 |
Subsurface slow release (lined) |
| 1011-3 |
2 |
Subsurface slow release (lined) |
| 1267-2 |
2 |
Subsurface slow release (lined) |
| 1206-2 |
2 |
Subsurface slow release (lined) |
| 1129-4 |
2 |
Subsurface slow release (lined) |
| 1287-6 |
2 |
Subsurface slow release (lined) |
| 1051-17 |
1 |
Subsurface slow release (unlined) |
| 595-10 |
1 |
Subsurface slow release (unlined) |
| 1209-6 |
1 |
Subsurface slow release (unlined) |
| 1275-2 |
1 |
Subsurface slow release (unlined) |
| 1062-5 |
1 |
Subsurface slow release (unlined) |
| 1137-6 |
1 |
Subsurface slow release (unlined) |
| 1070-11 |
2 |
Subsurface slow release (unlined) |
| 1265-11 |
2 |
Subsurface slow release (unlined) |
| 989-1 |
2 |
Subsurface slow release (unlined) |
| 1359-2 |
2 |
Subsurface slow release (unlined) |
| 1051-3 |
2 |
Subsurface slow release (unlined) |
To generate monitoring systems, we developed a semi-random approach
that utilizes a binning technique. This approach involves assigning
systems to clusters based on their specific type, with the binning
process utilizing a combination of design metrics to group similar
systems together. To ensure a representative sample of systems, the
random selection process chooses a proportional number of systems from
each cluster and system type based on their size.
LS0tDQp0aXRsZTogIlNNUCBQb3B1bGF0aW9uIEFzc2VzbWVudCINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KYXV0aG9yOiAiRmFyc2hhZCBFYnJhaGltaSwgRmViLzcvMjAyMyINCi0tLQ0KDQoqKkRldmVsb3BpbmcgYSBTdHJhdGlmaWVkIFNhbXBsaW5nIFNjaGVtZSBmb3IgdGhlIFVubW9uaXRvcmVkIFN5c3RlbXMgVXNpbmcgSy1tZWFucyBDbHVzdGVyaW5nIE1ldGhvZCoqDQoNCioqQmFja2dyb3VuZCoqDQoNClRoZSBhaW0gb2YgdGhpcyBhbmFseXNpcyBpcyB0byBhbmFseXplIFNNUCBwb3B1bGF0aW9uIHVzaW5nIENXTCBtb25pdG9yaW5nIGRhdGEsIGNhdGVnb3JpemUgaXQsIGFuZCBpZGVudGlmeSB1bmRlcnJlcHJlc2VudGVkIFNNUCBmZWF0dXJlcyBpbiBvdXIgZGF0YSBzZXQuIFdlIHdpbGwgYWxzbyB1c2UgY2x1c3RlcmluZyB0byBncm91cCBzeXN0ZW1zIHdpdGggc2ltaWxhciBjaGFyYWN0ZXJpc3RpY3MgYW5kIGRldmVsb3AgYSBzdHJhdGlmaWVkIHNhbXBsaW5nIHNjcmlwdCB0byByYW5kb21seSBzZWxlY3QgYSByZXByZXNlbnRhdGl2ZSBzYW1wbGUgb2Ygc3lzdGVtcyBwcm9wb3J0aW9uYWwgdG8gdGhlaXIgY2x1c3RlciBhbmQgc3lzdGVtIHR5cGUuIFRoaXMgd2lsbCBlbnN1cmUgYSBjb21wcmVoZW5zaXZlIGRhdGEgc2V0IGNvdmVyaW5nIGFsbCB0eXBlcyBvZiBTTVBzIHdpdGggdmFyeWluZyBkZXNpZ24gbWV0cmljcyBzdWNoIGFzIGxvYWRpbmcgcmF0aW9zLCBkcmFpbmFnZSBhcmVhLCBhbmQgc3Rvcm0gc2l6ZSBtYW5hZ2VtZW50LiBUaGUgb3V0cHV0IG9mIHRoaXMgYW5hbHlzaXMgd2lsbCBoZWxwIHRoZSBkYXRhIGNvbGxlY3Rpb24gdGVhbSB3aXRoIHRoZWlyIGZ1dHVyZSBzaXRlIHNlbGVjdGlvbi4NCg0KMS4gICoqRmluZGluZyB0aGUgb3Zlci1yZXByZXNlbnRlZC91bmRlci1yZXByZXNlbnRlZCBTTVBzKioNCg0KYGBge3Igc2VjdGlvbiAxLCBMb2FkaW5nIGxpYnJhcmllcyBhbmQgcXVlcnlpbmcgc21wIGRhdGEgfQ0KI0xpYnJhcmllcw0KICAgICAgbGlicmFyeShvZGJjKQ0KICAgICAgbGlicmFyeShEQkkpDQogICAgICBsaWJyYXJ5KGx1YnJpZGF0ZSkNCiAgICAgIGxpYnJhcnkodGlkeXZlcnNlKQ0KICAgICAgbGlicmFyeShzdGF0cykNCiAgICAgIGxpYnJhcnkoZ3JpZEV4dHJhKQ0KICAgICAgbGlicmFyeShncmlkKQ0KICAgICAgbGlicmFyeShndGFibGUpDQogICAgICBsaWJyYXJ5KGdndGV4dCkNCiAgICAgIGxpYnJhcnkoZHBseXIpDQogICAgICBsaWJyYXJ5KGdncGxvdDIpDQogICAgICBsaWJyYXJ5KGtuaXRyKQ0KICAgICAgbGlicmFyeShwbG90bHkpDQogICAgICBsaWJyYXJ5KGNsdXN0ZXIpDQogICAgICBsaWJyYXJ5KGZhY3RvZXh0cmEpDQojIERCIFBHMTQNCiAgICBjb24gPC0gZGJDb25uZWN0KG9kYmM6Om9kYmMoKSwgZHNuID0gIm1hcnMxNF9kYXRhIiwgdWlkID0gU3lzLmdldGVudigic2hpbnlfdWlkIiksIHB3ZCA9IFN5cy5nZXRlbnYoInNoaW55X3B3ZCIpLCBNYXhMb25nVmFyY2hhclNpemUgPSA4MTkwKQ0KDQojR2V0dGluZyBsaXN0IG9mIFNNUHMNCiAgICBjd2xfc21wIDwtIGRiR2V0UXVlcnkoY29uLCJTRUxFQ1QgRElTVElOQ1Qgc21wX2lkDQogICAgICAgICAgIEZST00gZmllbGR3b3JrLnZpd19kZXBsb3ltZW50X2Z1bGxfY3dsIikNCiAgICANCiNHZXR0aW5nIHRoZSBncmVlbml0IGluZm8NCiAgICBzbXBiZHYgPC0gZGJHZXRRdWVyeShjb24sIlNFTEVDVCAqIEZST00gZXh0ZXJuYWwudGJsX3NtcGJkdiIpDQogICAgc3lzYmR2IDwtIGRiR2V0UXVlcnkoY29uLCJTRUxFQ1QgKiBGUk9NIGV4dGVybmFsLnRibF9zeXN0ZW1iZHYiKQ0KICAgIGdyZWVuaXRfdW5pZmllZCA8LWRiR2V0UXVlcnkoY29uLCJTRUxFQ1QgKiBGUk9NIGV4dGVybmFsLnZpd19ncmVlbml0X3VuaWZpZWQiKQ0KDQojIGpvaW4gdGhlIGdyZWVuaXQgdGFibGUgd2l0aCBvdXIgc21wIGxpc3QNCiAgICBzbXBfZmVhdHVyZXMgPC0gY3dsX3NtcCAlPiUgDQogICAgICBpbm5lcl9qb2luKHNtcGJkdiwgYnk9InNtcF9pZCIpDQojIFVubW9uaXRvcmVkIFNNUHMNCiAgICB1bm1vbml0b3JlZF9zbXAgPC0gcmVhZC5jc3YoIkM6XFxVc2Vyc1xcRmFyc2hhZC5FYnJhaGltaVxcT25lRHJpdmUgLSBDaXR5IG9mIFBoaWxhZGVscGhpYVxcR2l0aHViIFByb2plY3RzXFxzbXAtcG9wdWxhdGlvbi1hc3Nlc21lbnRcXHVubW9uaXRvcmVkX3NpdGVzLmNzdiIpICU+JQ0KICAgICAgc2VsZWN0KHNtcF9pZCA9IFNNUC5JRCkgJT4lDQogICAgICBkaXN0aW5jdCgpDQojIHVubW9uaXRvcmVkIHNtcF9mZWF0dXJlcw0KICAgIHVubW9uaXRvcmVkX3NtcF9mZWF0dXJlcyA8LSB1bm1vbml0b3JlZF9zbXAgJT4lDQogICAgICBpbm5lcl9qb2luKHNtcGJkdiwgYnk9InNtcF9pZCIpDQpgYGANCg0KVGhlIGNvZGUgY2FsY3VsYXRlcyB0aGUgZGlzdHJpYnV0aW9uIG9mIFNNUCB0eXBlcyB3aXRoIENXTCBkYXRhIGluIHRoZSBNQVJTIGRhdGFiYXNlLCBwcm92aWRpbmcgYW4gaW5pdGlhbCB1bmRlcnN0YW5kaW5nIG9mIHRoZSBkaXN0cmlidXRpb24gb2YgU01QIHN1YnR5cGVzLg0KDQpgYGB7ciBzZWN0aW9uIDIsIE1vbml0b3JlZCBTTVAgYnJlYWstZG93biBiYXNlZCBvbiB0eXBlfQ0KI251bWJlciBvZiBzbXAgdHlwZXMNCnNtcF90eXBlX2JyZWFrIDwtIHNtcF9mZWF0dXJlcyAlPiUgDQogIGdyb3VwX2J5KHNtcF9zbXB0eXBlKSAlPiUNCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKSAlPiUNCiAgc2VsZWN0KFR5cGUgPSBzbXBfc21wdHlwZSwgY291bnQpDQpgYGANCg0KYGBge3Igc2VjdGlvbiAzIHBpZSBjaGFydCBvZiBtb25pdG9yZWQgU01QcywgZWNobz1GYWxzZX0NCmdncGxvdChzbXBfdHlwZV9icmVhaywgYWVzKHg9IiIsIHk9IGNvdW50LCBmaWxsPVR5cGUpKStnZW9tX2Jhcih3aWR0aCA9IDEsIHN0YXQgPSAiaWRlbnRpdHkiKStjb29yZF9wb2xhcigieSIsIHN0YXJ0PTApDQpgYGANCg0KYGBge3Igc2VjdGlvbiA0LCBsb29raW5nIGF0IHRoZSB1bm1vbml0b3JlZCBTTVBzIHRvIHNlZSB0aGUgYnJlYWstZG93bn0NCiNudW1iZXIgb2YgdW5tb25pdG9yZWQgc21wcw0KdW5tb25pdG9yZWRfc21wX3R5cGVfYnJlYWsgPC0gdW5tb25pdG9yZWRfc21wX2ZlYXR1cmVzICU+JSANCiAgZ3JvdXBfYnkoc21wX3NtcHR5cGUpICU+JQ0KICBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JQ0KICBzZWxlY3QoVHlwZSA9IHNtcF9zbXB0eXBlLCBjb3VudCkNCmBgYA0KDQpTaW1pbGFyIGFuYWx5c2lzIGlzIHBlcmZvcm1lZCBiZWxvdyB0byBjYWxjdWxhdGUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBTTVAgdHlwZXMgaW4gdGhlIGVudGlyZSBwb3B1bGF0aW9uIG9mIFNNUCBpbmNsdWRpbmcgdGhlIG1vbml0b3JlZCBhbmQgdW5tb25pdG9yZWQgU01Qcy4NCg0KYGBge3Igc2V0aW9uIDUgcGllIGNoYXJ0IG9mIHVubW9uaXRvcmVkIFNNUHMsIGVjaG89RkFMU0V9DQpnZ3Bsb3QodW5tb25pdG9yZWRfc21wX3R5cGVfYnJlYWssIGFlcyh4PSIiLCB5PSBjb3VudCwgZmlsbD1UeXBlKSkrZ2VvbV9iYXIod2lkdGggPSAxLCBzdGF0ID0gImlkZW50aXR5IikrY29vcmRfcG9sYXIoInkiLCBzdGFydD0wKQ0KYGBgDQoNClRoZSBhbmFseXNpcyBhaW1zIHRvIGRldGVybWluZSB0aGUgb3Zlci1yZXByZXNlbnRlZCAobm9ybWFsaXplZCBwZXJjZW50IFw+IDEpIGFuZCB1bmRlci1yZXByZXNlbnRlZCAobm9ybWFsaXplZCBwZXJjZW50IFw8IDEpIFNNUCB0eXBlcyBieSBkaXZpZGluZyB0aGUgZnJhY3Rpb24gb2YgYSBzcGVjaWZpYyBTTVAgdHlwZSBpbiB0aGUgbW9uaXRvcmVkIGRhdGEgYnkgdGhlIGZyYWN0aW9uIGluIHRoZSBlbnRpcmUgcG9wdWxhdGlvbi4NCg0KYGBge3Igc2VjdGlvbiA2IGZ1bGwgam9pbiB0aGUgdW5tb25pdG9yZWQgYW5kIG1vbml0b3JlZCwgZWNobz1GQUxTRX0NCiNmdWxsIGpvaW4gdGhlIHVubW9uaXRvcmVkIGFuZCBtb25pcm9yZWQgYW5kIGNyZWF0ZSBhIGNvbHVtbiB0byBzdW0gdGhlbSB1cA0Kc21wX2JyZWFrZHdvbl9mdWxsIDwtIHNtcF90eXBlX2JyZWFrICU+JQ0KICBmdWxsX2pvaW4odW5tb25pdG9yZWRfc21wX3R5cGVfYnJlYWssIGJ5PSJUeXBlIikgJT4lDQogIHNlbGVjdChUeXBlLCBjb3VudF9tb25pdG9yZWQgPSBjb3VudC54LCBjb3VudF91bm1vbml0b3JlZCA9IGNvdW50LnkpDQoNCiNyZXBsYWNlIG5hIHdpdGggemVybw0Kc21wX2JyZWFrZHdvbl9mdWxsW2lzLm5hKHNtcF9icmVha2R3b25fZnVsbCldIDwtIDANCg0KI211dGF0ZSB0aGUgdGhpcmQgY29sdW1uDQpzbXBfYnJlYWtkd29uX2Z1bGwgPC0gc21wX2JyZWFrZHdvbl9mdWxsICU+JQ0KICBtdXRhdGUoc3VtX2FsbCA9IGNvdW50X21vbml0b3JlZCArIGNvdW50X3VubW9uaXRvcmVkKQ0KDQojZGF0YSBmcmFtZSBmb3IgcGxvdHRpbmcgKG9ubHkgdHdvIGNvbHVtbnMpDQpwaXZvdF9kZiA8LSBzbXBfYnJlYWtkd29uX2Z1bGwgJT4lDQogIHNlbGVjdChUeXBlLCBjb3VudF9tb25pdG9yZWQsIHN1bV9hbGwpDQoNCmRmX2xvbmdlciA8LSBwaXZvdF9sb25nZXIocGl2b3RfZGYsIGNvbHMgPSBjKCJjb3VudF9tb25pdG9yZWQiLCAic3VtX2FsbCIpLCBuYW1lc190byA9ICJjYXRlZ29yeSIsIHZhbHVlc190byA9ICJjb3VudCIpDQoNCiNQbG90DQpnZ3Bsb3QoZGZfbG9uZ2VyLGFlcyh4ID0gVHlwZSwgeSA9IGNvdW50LCBmaWxsPSBjYXRlZ29yeSkpICsNCiAgICAgIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249cG9zaXRpb25fZG9kZ2UoKSkrDQogICAgICB0aGVtZShheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9NiwgYW5nbGUgPSAzNSksDQogICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTApKQ0KDQpgYGANCg0KYGBge3Igc2VjdGlvbiA3LCBhZGRpbmcgdGhlIG1vbml0b3JlZCBhbmQgdW5tb25pb3JlZH0NCiNjb21iaW5pbmcgYWxsIHNtcHMgKG1vbml0b3JlZCBhbmQgdW5tb25pdG9yZWQpIGFuZCBnZXQgZnJhY3Rpb25zIGJhc2VkIG9uIHR5cGUgb2Ygc21wLWZpbmFsbHkgbm9ybWFsaXplIHRoZW0NCnRvdGFsX3NtcCA8LSB1bm1vbml0b3JlZF9zbXBfdHlwZV9icmVhayAlPiUNCiAgZnVsbF9qb2luKHNtcF90eXBlX2JyZWFrLCBieT0iVHlwZSIpDQp0b3RhbF9zbXBbaXMubmEodG90YWxfc21wKV0gPC0gMA0KdG90YWxfc21wIDwtIHRvdGFsX3NtcCAlPiUNCiAgbXV0YXRlKHRvdGFsX251bWJlciA9IGNvdW50LngrY291bnQueSkgJT4lDQogIHNlbGVjdChUeXBlLCB0b3RhbF9udW1iZXIpDQp0b3RhbF9zbXAgPC0gdG90YWxfc21wICU+JQ0KICAgIG11dGF0ZShzdW1fYWxsID0gc3VtKHRvdGFsX251bWJlcikpICU+JQ0KICAgIG11dGF0ZShwZXJjZW50X2FsbCA9ICh0b3RhbF9udW1iZXIvc3VtX2FsbCkqMTAwKQ0KDQojZ2V0IHRoZSBwZXJjZW50IG9mIG1vbml0b3JlZCBTTVANCnNtcF90eXBlX2JyZWFrX2ZyYWN0aW9uIDwtIHNtcF90eXBlX2JyZWFrICU+JQ0KICAgIG11dGF0ZShzdW1fYWxsID0gc3VtKGNvdW50KSkgJT4lDQogICAgbXV0YXRlKHBlcmNlbnRfbW9uaXRvcmVkID0gKGNvdW50L3N1bV9hbGwpKjEwMCkNCg0KI25vcm1hbGl6ZSB0aGUgbW9uaXRvcmVkIHNtcCBieSB0aGUgdG90YWwgc21wIHBlcmNlbnRzDQpub3JtYWxpemVkX2ZyYWN0aW9ucyA8LSBzbXBfdHlwZV9icmVha19mcmFjdGlvbiAlPiUNCiAgZnVsbF9qb2luKHRvdGFsX3NtcCwgYnk9IlR5cGUiKSAlPiUNCiAgbXV0YXRlKG5vcm1hbGl6ZWRfcGVyY2VudCA9IHBlcmNlbnRfbW9uaXRvcmVkL3BlcmNlbnRfYWxsKSAlPiUNCiAgc2VsZWN0KFR5cGUsIHBlcmNlbnRfbW9uaXRvcmVkLCBwZXJjZW50X2FsbCwgbm9ybWFsaXplZF9wZXJjZW50KQ0KDQpub3JtYWxpemVkX2ZyYWN0aW9uc1tpcy5uYShub3JtYWxpemVkX2ZyYWN0aW9ucyldIDwtIDANCmBgYA0KDQpgYGB7cn0NCiNjYWxjdWxhdGluZyB0aGUgbm9ybWFsaXplZCBwZXJjZW50YWdlIG9mIHNtcCB0eXBlcw0Ka2FibGUoYXJyYW5nZShub3JtYWxpemVkX2ZyYWN0aW9ucywgbm9ybWFsaXplZF9wZXJjZW50KSwgY2FwdGlvbiA9ICJOb3JtYWxpemVkIFNNUCBCcmVhay1kb3duIikNCmBgYA0KDQpUaGUgdGFibGUgYWJvdmUgc2hvd3MgVHJlZSB0cmVuY2hlcyBhcmUgb3Zlci1yZXByZXNlbnRlZCBpbiBvdXIgQ1dMIGRhdGEsIHdoaWxlIGJ1bXBvdXQsIHN3YWxlIGFuZCBwbGFudGVycyBhcmUgdW5kZXItcmVwcmVzZW50ZWQgKGR1ZSB0byBtb25pdG9yaW5nIGxpbWl0YXRpb24sIGV0YykuIEhvd2V2ZXIsIGl0IHNob3VsZCBiZSBub3RlZCB0aGF0IE1BUlMgZG9lcyBub3QgcGxhbiBvbiBtb25pdG9yaW5nIGdyZWVmIHJvb2ZzIGR1ZSB0byBsaW1pdGF0aW9ucyBvbiBhY2Nlc3MgdG8gdGhlc2Ugc3lzdGVtcyBhbmQgYSAxOjEgbG9hZGluZyByYXRpb3MuIFN0cm9td2F0ZXIgdHJlZXMsIHNtaWxhcmx5LCBkb2VzIG5vdCBoYXZlIGFueSBDV0wgZGF0YSBhbmQgdGhlIG1vbml0b3JpbmcgZWZmb3J0cyBhcmUgbGltaXRlZCB0byBDRVQgdGVzdHMgb25seS4gUG9yb3VzIHBhdmVtZW50cyBhcmUgYWxzbyBsaW1pdGVkIHRvIGluZmlsdHJhdGlvbiByYXRlIHRlc3RpbmcgYW5kIGhhdmUgbm8gQ1dMIGRhdGEuDQoNCjIuICAqKkNsdXN0ZXJpbmcgYXQgdGhlIHN5c3RlbS1sZXZlbCoqDQoNClRoZSBjdXJyZW50IHNlY3Rpb24gYWltcyB0byBjYXRlZ29yaXplIHRoZSBzeXN0ZW1zIHdpdGhpbiBlYWNoIGJyb2FkIHN1YnR5cGVzLiBUaGUgcmVhc29uIHRvIHBlcmZvcm0gdGhpcyBhdCB0aGUgc3lzdGVtIGxldmVsIGlzIHRoZSBhY2Nlc3NpYmlsaXR5IG9mIGRlc2lnbiBtZXRyaWNzIGF0IHRoZSBzeXN0ZW0gbGV2ZWwuIFRoZSBjbHVzdGVyaW5nIHdpdGhpbiBlYWNoIHR5cGUgb2Ygc3lzdGVtcyBpcyBkb25lIHRvIGVuc3VyZSB0aGUgc2FtcGxpbmcgYXBwcm9hY2ggcGlja3MgYW4gYXBwcm9wcmlhdGUgbnVtYmVyIG9mIHN5c3RlbXMgZnJvbSBzeXN0ZW1zIHBvc3Nlc3Npbmcgc2ltaWxhciBjaGFyYWN0ZXJpc3RpY3Mgd2l0aGluIHRoZXNlIHR5cGVzLiBGb3IgZXhhbXBsZSwgdGhlIHNhbXBsaW5nIG1ldGhvZCBzaG91bGQgc2VsZWN0IGEgbGFyZ2VyIG51bWJlciBvZiBCaW9pbmZpbHRyYXRpb24gc3lzdGVtcyB3aXRoIGhpZ2hlciBkcmFpbmFnZSBhcmVhIGlmIHRob3NlIHN5c3RlbXMgY29uc3RpdHV0ZSB0aGUgbWFqb3JpdHkgb2YgQmlvaW5maWx0cmF0aW9uIHN5c3RlbXMuDQoNClRoZSBiaW5uaW5nIGFwcHJvYWNoIGNvbnNpZGVycyBzZXZlcmFsIHN5c3RlbSBmZWF0dXJlcyBsaWtlIGxvYWRpbmcgcmF0aW9zLCBkcmFpbmFnZSBhcmVhLCBhbmQgc3Rvcm0gc2l6ZSBtYW5hZ2VtZW50LiBUaGUgY29kZSBjaHVua3MgYmVsb3cgZGVtb25zdHJhdGUgYSBjbHVzdGVyaW5nIG1ldGhvZCBhcHBsaWVkIHRvIHVubW9uaXRvcmVkIHN5c3RlbXMsIHJlZ2FyZGxlc3Mgb2Ygc3VidHlwZXMsIGFzIGFuIGV4YW1wbGUgb2YgaG93IGl0IGNhbiBncm91cCBzeXN0ZW1zIGJhc2VkIHNvbGVseSBvbiB0aGVzZSB0aHJlZSBkZXNpZ24gbWV0cmljcy4gVGhlIGNsdXN0ZXJpbmcgd2lsbCB0aGVuIGJlIHJlcGVhdGVkIHdpdGhpbiBlYWNoIHN1YnR5cGUgdG8gcHJvdmlkZSBhIG1vcmUgcmVsZXZhbnQgbWV0aG9kIGZvciBzYW1wbGluZy4NCg0KYGBge3Igc2VjdGlvbiA4LCByYXdzdG9ybXNpemVtYW5hZ2VkLWluICwgc3lzX2ltcGVydmRhX2Z0MiwgYW5kIGxvYWRpbmcgcmF0aW8gbWV0cmljcyBnYXRoZXJpbmd9DQojcmF3c3Rvcm1zaXplbWFuYWdlZC1pbiAsIHN5c19pbXBlcnZkYV9mdDIsIGFuZCBsb2FkaW5nIHJhdGlvIG1ldHJpY3MgZ2F0aGVyaW5nDQoNCnRhYmxlX2FsbCA8LSB1bm1vbml0b3JlZF9zbXBfZmVhdHVyZXMgJT4lDQogIGxlZnRfam9pbihzeXNiZHYsIGJ5PSJzeXN0ZW1faWQiKSAlPiUNCiAgbXV0YXRlKGxvYWRpbmdfcmF0aW8gPSBzeXNfbHJ0b3RhbGRhX2Z0MiApDQpgYGANCg0KSy1tZWFucyBjbHVzdGVyaW5nIGlzIGFuIHVuc3VwZXJ2aXNlZCBsZWFybmluZyBtZXRob2QgdGhhdCBncm91cHMgZGF0YSBwb2ludHMgYmFzZWQgb24gdGhlaXIgcHJveGltaXR5IHRvIGNlbnRyb2lkcywgd2hpY2ggYXJlIGNhbGN1bGF0ZWQgYXMgdGhlIG1lYW4gb2YgYWxsIHBvaW50cyBpbiBhIGNsdXN0ZXIuIFRoZSBhbGdvcml0aG0gc3RhcnRzIGJ5IHJhbmRvbWx5IHNlbGVjdGluZyBpbml0aWFsIGNlbnRyb2lkcywgYW5kIHRoZW4gcmVwZWF0ZWRseSBhZGp1c3RzIHRoZW0gdW50aWwgY29udmVyZ2VuY2UgaXMgYWNoaWV2ZWQNCg0KVGhlIG9wdGltYWwgbnVtYmVyIG9mIGNsdXN0ZXJzIGlzIGRldGVybWluZWQgdXNpbmcgdGhlIEVsYm93IG1ldGhvZC4gSW4gc3VtbWFyeSwgdGhpcyBtZXRob2QgcGlja3MgdGhlIG51bWJlciBvZiBjbHVzdGVycyBpbiB3aGljaCBhIHRoZSBzbG9wZSBvZiB0aGUgbGluZXMgY2hhbmdlcyBtb3JlIGRyYXN0aWNhbGx5IChjbHVzdGVyIDQgaW4gdGhlIGZvbGxvd2luZyBncmFwaCkuDQoNCmBgYHtyIHNlY3Rpb24gOX0NCiNDbHVzdHJpbmcgYW5hbHlzaXMgYmFzZWQgb24gMyBmZWF0dXJlcyBvZiBkcmFpbmFnZSBhcmVhLCBsb2FkaW5nIHJhdGlvLCBhbmQgc3Rvcm0gc2l6ZWQgbWFuYWdlZA0KDQpjbHVzdGVyZWRfbW9uaW9yZWQgPC0gdGFibGVfYWxsICU+JQ0KICBzZWxlY3Qoc3lzdGVtX2lkLCBzeXNfaW1wZXJ2ZGFfZnQyLCBsb2FkaW5nX3JhdGlvLCBzeXNfcmF3c3Rvcm1zaXplbWFuYWdlZF9pbikgJT4lDQogIG5hLm9taXQoKSAlPiUNCiAgZGlzdGluY3QoKQ0KDQojbm9ybWFsemUgdXNpbmcgbWVhbi1zZCBzdGFuZGFyZGl6YXRpb24NCm5vcm1hbGl6ZWRfY2x1c3RlciA8LSBjbHVzdGVyZWRfbW9uaW9yZWQNCm5vcm1hbGl6ZWRfY2x1c3RlclssMjo0XSA8LSBzY2FsZShjbHVzdGVyZWRfbW9uaW9yZWRbLDI6NF0pDQoNCiNFbGJvdyBtZXRob2Qgc2hvd3MgYSBzdWJ0bGUgZWxib3cgYXQgY2x1c3RlciA0LWhlbmNlIDQgY2x1c3RlcnMgaXMgb3B0aW11bQ0KI251bWJlciBvZiBjbHVzdGVycw0KZnZpel9uYmNsdXN0KG5vcm1hbGl6ZWRfY2x1c3RlclssMjo0XSwga21lYW5zLCBtZXRob2QgPSAid3NzIikNCg0KI2stbWVhbnMgDQojIENsdXN0ZXIgdXNpbmcga21lYW5zIHdpdGggZml2ZSBjbHVzdGVycw0KY2x1c3Rlcl9zb2x1dGlvbiA8LSBrbWVhbnMobm9ybWFsaXplZF9jbHVzdGVyWywyOjRdLCBjZW50ZXJzID0gNCkNCg0KIyBTdG9yZSB0aGUgY2x1c3RlciBhc3NpZ25tZW50cyBiYWNrIGludG8gdGhlIGNsdXN0ZXJpbmcgZGF0YSBmcmFtZSBvYmplY3QNCmNsdXN0ZXJlZF9tb25pb3JlZCRjbHVzdGVyIDwtZmFjdG9yKGNsdXN0ZXJfc29sdXRpb24kY2x1c3RlcikgDQoNCiMgTG9vayBhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIGNsdXN0ZXIgYXNzaWdubWVudHMNCnRhYmxlKGNsdXN0ZXJlZF9tb25pb3JlZCRjbHVzdGVyKQ0KYGBgDQoNClRoZSBhYm92ZSB0YWJsZSBkaXNwbGF5cyB0aGUgbnVtYmVyIG9mIHN5c3RlbXMgaW4gZWFjaCBjbHVzdGVyLiBUaGUgc3Vic2VxdWVudCB0YWJsZXMgYWltIHRvIGRldGVybWluZSB0aGUgYXZlcmFnZSBvZiBzeXN0ZW0gZGVzaWduIG1ldHJpY3MgaW4gZWFjaCBjYXRlZ29yeS4gVGhlIGRhdGEgY2FuIGJlIGFuYWx5emVkIGJ5IGV4YW1pbmluZyBlYWNoIGNhdGVnb3J5OyBmb3IgaW5zdGFuY2UsIENsdXN0ZXIgRm91ciBlbmNvbXBhc3NlcyBzeXN0ZW1zIHdpdGggYSBsYXJnZSBkcmFpbmFnZSBhcmVhLCBjYXBhYmxlIG9mIGhhbmRsaW5nIGxhcmdlciBzdG9ybSBzaXplcyAoXD4zIGluY2gvaHIpLg0KDQpgYGB7ciBzZWN0aW9uIDEwIGFuYWx5emluZyB0aGUgY2x1c3RlciBmZWF0dXJlc30NCiMgR3JvdXAgYnkgdGhlIGNsdXN0ZXIgYXNzaWdubWVudCBhbmQgY2FsY3VsYXRlIGF2ZXJhZ2VzDQpzeXNfY2x1c19hdmcgPC0gY2x1c3RlcmVkX21vbmlvcmVkICU+JQ0KICAgIGdyb3VwX2J5KGNsdXN0ZXIpICU+JQ0KICAgIHN1bW1hcml6ZV9pZihpcy5udW1lcmljLCBtZWFuKQ0KDQojIFZpZXcgdGhlIHJlc3VsdGluZyB0YWJsZQ0Ka2FibGUoc3lzX2NsdXNfYXZnKQ0KYGBgDQoNCmBgYHtyIDJEIFBsb3R9DQpnZ3Bsb3QoZGF0YSA9IGNsdXN0ZXJlZF9tb25pb3JlZCwgYWVzKHggPSBsb2FkaW5nX3JhdGlvLCB5ID0gc3lzX3Jhd3N0b3Jtc2l6ZW1hbmFnZWRfaW4sIGNvbG9yID0gY2x1c3RlcikpICsNCiAgICBnZW9tX3BvaW50KCkNCmBgYA0KDQpgYGB7ciAzRCBwbG90fQ0KcGxvdF9seShjbHVzdGVyZWRfbW9uaW9yZWQsIHggPSB+c3lzX2ltcGVydmRhX2Z0MiwgeSA9IH5sb2FkaW5nX3JhdGlvLCB6ID0gfnN5c19yYXdzdG9ybXNpemVtYW5hZ2VkX2luKSAlPiUNCiAgYWRkX21hcmtlcnMoY29sb3IgPSB+Y2x1c3RlcikNCmBgYA0KDQpJbiBvcmRlciB0byBjcmVhdGUgYSBtb3JlIHBlcnRpbmVudCBjbHVzdGVyaW5nIG1ldGhvZCB0aGF0IGRpc3Rpbmd1aXNoZXMgYmV0d2VlbiBnZW5lcmFsIHR5cGVzIG9mIHN5c3RlbXMgKGxpbmVkIHZzLiB1bmxpbmVkIG9yIGJpb3JldGVudGlvbiB2cy4gYmlvaW5maWx0cmF0aW9uKSwgdGhlIHVubW9uaXRvcmVkIHN5c3RlbXMgd2VyZSBkaXZpZGVkIGludG8gYnJvYWQgY2F0ZWdvcmllcywgaW5jbHVkaW5nIEJpb2luZmlsdHJhdGlvbiwgQmlvcmV0ZW50aW9uIChsaW5lZCksIEJpb3JldGVudGlvbiAodW5saW5lZCksIFN1YnN1cmZhY2UgSW5maWx0cmF0aW9uLCBTdWJzdXJmYWNlIFNsb3cgUmVsZWFzZSAobGluZWQpLCBhbmQgU3Vic3VyZmFjZSBTbG93IFJlbGVhc2UgKHVubGluZWQpIHVzaW5nIHRoZSBzeXN0ZW0tbGV2ZWwgZmVhdHVyZSBjYWxsZWQgc3lzX21vZGVsaW5wdXRjYXRlZ29yeS4gVGhlIGNvZGUgY2h1bmsgYmVsb3cgZGlzcGxheXMgYSBicmVha2Rvd24gb2YgdGhlc2UgU01Qcy4gVGhlIGNsdXN0ZXJpbmcgYWxnb3JpdGhtIHdhcyByZXBlYXRlZCBmb3IgZWFjaCBvZiB0aG9zZSBncm91cHMgc2ltaWxhcmx5LiBQbGVhc2Ugbm90ZSB0aGF0IEdyZWVuIFJvb2ZzIGFuZCBQZXJtZWFibGUgUGF2ZW1lbnRzIHdlcmUgbm90IGluY2x1ZGVkIGR1ZSB0byBhIGxvdyBudW1iZXIgb2Ygc3lzdGVtcy4NCg0KYGBge3Igc2VjdGlvbiAxMSBzeXNfbW9kZWxpbnB1dGNhdGVnb3J5IGlzIHVzZWQgdG8gY2F0ZWdvcml6ZSBzeXN0ZW1zfQ0KI3N5c19tb2RlbGlucHV0Y2F0ZWdvcnkgY2F0ZWdvcmllcw0Kc3lzX2NhdCA8LSB0YWJsZV9hbGwgJT4lDQogIHNlbGVjdChzeXN0ZW1faWQsIHN5c19tb2RlbGlucHV0Y2F0ZWdvcnkpJT4lDQogIGRpc3RpbmN0ICU+JQ0KICBncm91cF9ieShzeXNfbW9kZWxpbnB1dGNhdGVnb3J5KSAlPiUNCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKQ0KDQprYWJsZShzeXNfY2F0KQ0KYGBgDQoNCioqLUJpb2luZmlsdHJhdGlvbiBDbHVzdGVyaW5nOioqDQoNCmBgYHtyIHNlY3Rpb24gMTIgY2x1c3RlcmluZyBCaW9pbmZpbHRyYXRpb24gdHlwZSBzeXN0ZW1zIH0NCg0KY2x1c3RlcmVkX0Jpb2luZmlsdHJhdGlvbiA8LSB0YWJsZV9hbGwgJT4lDQogIGZpbHRlcihzeXNfbW9kZWxpbnB1dGNhdGVnb3J5ID09ICJCaW9pbmZpbHRyYXRpb24iKSAlPiUNCiAgc2VsZWN0KHN5c3RlbV9pZCwgc3lzX2ltcGVydmRhX2Z0MiwgbG9hZGluZ19yYXRpbywgc3lzX3Jhd3N0b3Jtc2l6ZW1hbmFnZWRfaW4sIHN5c19tb2RlbGlucHV0Y2F0ZWdvcnkpICU+JQ0KICBuYS5vbWl0KCkgJT4lDQogIGRpc3RpbmN0KCkNCg0KI25vcm1hbHplIHVzaW5nIG1lYW4tc2Qgc3RhbmRhcmRpemF0aW9uDQpub3JtYWxpemVkX2NsdXN0ZXIgPC0gY2x1c3RlcmVkX0Jpb2luZmlsdHJhdGlvbg0Kbm9ybWFsaXplZF9jbHVzdGVyWywyOjRdIDwtIHNjYWxlKGNsdXN0ZXJlZF9CaW9pbmZpbHRyYXRpb25bLDI6NF0pDQoNCiNFbGJvdyBtZXRob2Qgc2hvd3MgYSBzdWJ0bGUgZWxib3cgYXQgY2x1c3RlciA0LWhlbmNlIDQgY2x1c3RlcnMgaXMgb3B0aW11bQ0KI251bWJlciBvZiBjbHVzdGVycw0KZnZpel9uYmNsdXN0KG5vcm1hbGl6ZWRfY2x1c3RlclssMjo0XSwga21lYW5zLCBtZXRob2QgPSAid3NzIikNCg0KI2stbWVhbnMgDQojIENsdXN0ZXIgdXNpbmcga21lYW5zIHdpdGggZml2ZSBjbHVzdGVycw0KY2x1c3Rlcl9zb2x1dGlvbiA8LSBrbWVhbnMobm9ybWFsaXplZF9jbHVzdGVyWywyOjRdLCBjZW50ZXJzID0gNCkNCg0KIyBTdG9yZSB0aGUgY2x1c3RlciBhc3NpZ25tZW50cyBiYWNrIGludG8gdGhlIGNsdXN0ZXJpbmcgZGF0YSBmcmFtZSBvYmplY3QNCmNsdXN0ZXJlZF9CaW9pbmZpbHRyYXRpb24kY2x1c3RlciA8LWZhY3RvcihjbHVzdGVyX3NvbHV0aW9uJGNsdXN0ZXIpIA0KDQojIExvb2sgYXQgdGhlIGRpc3RyaWJ1dGlvbiBvZiBjbHVzdGVyIGFzc2lnbm1lbnRzDQp0YWJsZShjbHVzdGVyZWRfQmlvaW5maWx0cmF0aW9uJGNsdXN0ZXIpDQoNCiMgR3JvdXAgYnkgdGhlIGNsdXN0ZXIgYXNzaWdubWVudCBhbmQgY2FsY3VsYXRlIGF2ZXJhZ2VzDQpzeXNfY2x1c19hdmcgPC0gY2x1c3RlcmVkX0Jpb2luZmlsdHJhdGlvbiAlPiUNCiAgICBncm91cF9ieShjbHVzdGVyKSAlPiUNCiAgICBzdW1tYXJpemVfaWYoaXMubnVtZXJpYywgbWVhbikNCg0KIyBWaWV3IHRoZSByZXN1bHRpbmcgdGFibGUNCmthYmxlKHN5c19jbHVzX2F2ZykNCg0KIzMgRCBwbG90DQpwbG90X2x5KGNsdXN0ZXJlZF9CaW9pbmZpbHRyYXRpb24sIHggPSB+c3lzX2ltcGVydmRhX2Z0MiwgeSA9IH5sb2FkaW5nX3JhdGlvLCB6ID0gfnN5c19yYXdzdG9ybXNpemVtYW5hZ2VkX2luKSAlPiUNCiAgYWRkX21hcmtlcnMoY29sb3IgPSB+Y2x1c3RlcikNCmBgYA0KDQoqKi1CaW9yZXRlbnRpb24gKGxpbmVkKSBjbHVzdGVyaW5nLiBOb3RlIHRoYXQgbG9hZGluZyByYXRpbyBmaWVsZCBpcyBub3QgYXBwbGljYWJsZSBoZXJlOioqDQoNCmBgYHtyIHNlY3Rpb24gMTMgQmlvcmV0ZW50aW9uIGxpbmVkIGNsdXN0ZXJpbmd9DQoNCmNsdXN0ZXJlZF9CaW9yZXRlbl9saW5lZCA8LSB0YWJsZV9hbGwgJT4lDQogIGZpbHRlcihzeXNfbW9kZWxpbnB1dGNhdGVnb3J5ID09ICJCaW9yZXRlbnRpb24gKGxpbmVkKSIpICU+JQ0KICBzZWxlY3Qoc3lzdGVtX2lkLCBzeXNfaW1wZXJ2ZGFfZnQyLCBzeXNfcmF3c3Rvcm1zaXplbWFuYWdlZF9pbiwgc3lzX21vZGVsaW5wdXRjYXRlZ29yeSkgJT4lDQogIG5hLm9taXQoKSAlPiUNCiAgZGlzdGluY3QoKQ0KDQojbm9ybWFsemUgdXNpbmcgbWVhbi1zZCBzdGFuZGFyZGl6YXRpb24NCm5vcm1hbGl6ZWRfY2x1c3RlciA8LSBjbHVzdGVyZWRfQmlvcmV0ZW5fbGluZWQNCm5vcm1hbGl6ZWRfY2x1c3RlclssMjozXSA8LSBzY2FsZShjbHVzdGVyZWRfQmlvcmV0ZW5fbGluZWRbLDI6M10pDQoNCiNFbGJvdyBtZXRob2Qgc2hvd3MgYSBzdWJ0bGUgZWxib3cgYXQgY2x1c3RlciA0LWhlbmNlIDQgY2x1c3RlcnMgaXMgb3B0aW11bQ0KI251bWJlciBvZiBjbHVzdGVycw0KZnZpel9uYmNsdXN0KG5vcm1hbGl6ZWRfY2x1c3RlclssMjozXSwga21lYW5zLCBtZXRob2QgPSAid3NzIikNCg0KI2stbWVhbnMgDQojIENsdXN0ZXIgdXNpbmcga21lYW5zIHdpdGggZml2ZSBjbHVzdGVycw0KY2x1c3Rlcl9zb2x1dGlvbiA8LSBrbWVhbnMobm9ybWFsaXplZF9jbHVzdGVyWywyOjNdLCBjZW50ZXJzID0gMikNCg0KIyBTdG9yZSB0aGUgY2x1c3RlciBhc3NpZ25tZW50cyBiYWNrIGludG8gdGhlIGNsdXN0ZXJpbmcgZGF0YSBmcmFtZSBvYmplY3QNCmNsdXN0ZXJlZF9CaW9yZXRlbl9saW5lZCRjbHVzdGVyIDwtZmFjdG9yKGNsdXN0ZXJfc29sdXRpb24kY2x1c3RlcikgDQoNCiMgTG9vayBhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIGNsdXN0ZXIgYXNzaWdubWVudHMNCnRhYmxlKGNsdXN0ZXJlZF9CaW9yZXRlbl9saW5lZCRjbHVzdGVyKQ0KDQojIEdyb3VwIGJ5IHRoZSBjbHVzdGVyIGFzc2lnbm1lbnQgYW5kIGNhbGN1bGF0ZSBhdmVyYWdlcw0Kc3lzX2NsdXNfYXZnIDwtIGNsdXN0ZXJlZF9CaW9yZXRlbl9saW5lZCAlPiUNCiAgICBncm91cF9ieShjbHVzdGVyKSAlPiUNCiAgICBzdW1tYXJpemVfaWYoaXMubnVtZXJpYywgbWVhbikNCg0KIyBWaWV3IHRoZSByZXN1bHRpbmcgdGFibGUNCmthYmxlKHN5c19jbHVzX2F2ZykNCg0KIzMgRCBwbG90DQpwbG90X2x5KGNsdXN0ZXJlZF9CaW9yZXRlbl9saW5lZCwgeCA9IH5zeXNfaW1wZXJ2ZGFfZnQyLCB5ID0gfnN5c19yYXdzdG9ybXNpemVtYW5hZ2VkX2luKSAlPiUNCiAgYWRkX21hcmtlcnMoY29sb3IgPSB+Y2x1c3RlcikNCmBgYA0KDQoqKi1CaW9yZXRlbnRpb24gKHVubGluZWQpIGNsdXN0ZXJpbmc6KioNCg0KYGBge3Igc2VjdGlvbiAxNCBCaW9yZXRlbnRpb24gdW5saW5lZCBjbHVzdGVyaW5nfQ0KDQpjbHVzdGVyZWRfQmlvcmV0ZW5fdW5saW5lZCA8LSB0YWJsZV9hbGwgJT4lDQogIGZpbHRlcihzeXNfbW9kZWxpbnB1dGNhdGVnb3J5ID09ICJCaW9yZXRlbnRpb24gKHVubGluZWQpIikgJT4lDQogIHNlbGVjdChzeXN0ZW1faWQsIHN5c19pbXBlcnZkYV9mdDIsIHN5c19yYXdzdG9ybXNpemVtYW5hZ2VkX2luLCBsb2FkaW5nX3JhdGlvLCBzeXNfbW9kZWxpbnB1dGNhdGVnb3J5KSAlPiUNCiAgbmEub21pdCgpICU+JQ0KICBkaXN0aW5jdCgpDQoNCiNub3JtYWx6ZSB1c2luZyBtZWFuLXNkIHN0YW5kYXJkaXphdGlvbg0Kbm9ybWFsaXplZF9jbHVzdGVyIDwtIGNsdXN0ZXJlZF9CaW9yZXRlbl91bmxpbmVkDQpub3JtYWxpemVkX2NsdXN0ZXJbLDI6NF0gPC0gc2NhbGUoY2x1c3RlcmVkX0Jpb3JldGVuX3VubGluZWRbLDI6NF0pDQoNCiNFbGJvdyBtZXRob2Qgc2hvd3MgYSBzdWJ0bGUgZWxib3cgYXQgY2x1c3RlciA0LWhlbmNlIDQgY2x1c3RlcnMgaXMgb3B0aW11bQ0KI251bWJlciBvZiBjbHVzdGVycw0KZnZpel9uYmNsdXN0KG5vcm1hbGl6ZWRfY2x1c3RlclssMjo0XSwga21lYW5zLCBtZXRob2QgPSAid3NzIikNCg0KI2stbWVhbnMgDQojIENsdXN0ZXIgdXNpbmcga21lYW5zIHdpdGggZml2ZSBjbHVzdGVycw0KY2x1c3Rlcl9zb2x1dGlvbiA8LSBrbWVhbnMobm9ybWFsaXplZF9jbHVzdGVyWywyOjRdLCBjZW50ZXJzID0gMykNCg0KIyBTdG9yZSB0aGUgY2x1c3RlciBhc3NpZ25tZW50cyBiYWNrIGludG8gdGhlIGNsdXN0ZXJpbmcgZGF0YSBmcmFtZSBvYmplY3QNCmNsdXN0ZXJlZF9CaW9yZXRlbl91bmxpbmVkJGNsdXN0ZXIgPC1mYWN0b3IoY2x1c3Rlcl9zb2x1dGlvbiRjbHVzdGVyKSANCg0KIyBMb29rIGF0IHRoZSBkaXN0cmlidXRpb24gb2YgY2x1c3RlciBhc3NpZ25tZW50cw0KdGFibGUoY2x1c3RlcmVkX0Jpb3JldGVuX3VubGluZWQkY2x1c3RlcikNCg0KIyBHcm91cCBieSB0aGUgY2x1c3RlciBhc3NpZ25tZW50IGFuZCBjYWxjdWxhdGUgYXZlcmFnZXMNCnN5c19jbHVzX2F2ZyA8LSBjbHVzdGVyZWRfQmlvcmV0ZW5fdW5saW5lZCAlPiUNCiAgICBncm91cF9ieShjbHVzdGVyKSAlPiUNCiAgICBzdW1tYXJpemVfaWYoaXMubnVtZXJpYywgbWVhbikNCg0KIyBWaWV3IHRoZSByZXN1bHRpbmcgdGFibGUNCmthYmxlKHN5c19jbHVzX2F2ZykNCg0KIzMgRCBwbG90DQpwbG90X2x5KGNsdXN0ZXJlZF9CaW9yZXRlbl91bmxpbmVkLCB4ID0gfnN5c19pbXBlcnZkYV9mdDIsIHkgPSB+c3lzX3Jhd3N0b3Jtc2l6ZW1hbmFnZWRfaW4sIHo9IH5sb2FkaW5nX3JhdGlvKSAlPiUNCiAgYWRkX21hcmtlcnMoY29sb3IgPSB+Y2x1c3RlcikNCmBgYA0KDQoqKi1TdWJzdXJmYWNlIGluZmlsdHJhdGlvbiBjbHVzdGVyaW5nOioqDQoNCmBgYHtyIHNlY3Rpb24gMTUgU3Vic3VyZmFjZSBpbmZpbHRyYXRpb24gY2x1c3RlcmluZ30NCg0KY2x1c3RlcmVkX3N1YnN1cmZhY2UgPC0gdGFibGVfYWxsICU+JQ0KICBmaWx0ZXIoc3lzX21vZGVsaW5wdXRjYXRlZ29yeSA9PSAiU3Vic3VyZmFjZSBpbmZpbHRyYXRpb24iKSAlPiUNCiAgc2VsZWN0KHN5c3RlbV9pZCwgc3lzX2ltcGVydmRhX2Z0Miwgc3lzX3Jhd3N0b3Jtc2l6ZW1hbmFnZWRfaW4sIGxvYWRpbmdfcmF0aW8sIHN5c19tb2RlbGlucHV0Y2F0ZWdvcnkpICU+JQ0KICBuYS5vbWl0KCkgJT4lDQogIGRpc3RpbmN0KCkNCg0KI25vcm1hbHplIHVzaW5nIG1lYW4tc2Qgc3RhbmRhcmRpemF0aW9uDQpub3JtYWxpemVkX2NsdXN0ZXIgPC0gY2x1c3RlcmVkX3N1YnN1cmZhY2UNCm5vcm1hbGl6ZWRfY2x1c3RlclssMjo0XSA8LSBzY2FsZShjbHVzdGVyZWRfc3Vic3VyZmFjZVssMjo0XSkNCg0KI0VsYm93IG1ldGhvZCBzaG93cyBhIHN1YnRsZSBlbGJvdyBhdCBjbHVzdGVyIDQtaGVuY2UgNCBjbHVzdGVycyBpcyBvcHRpbXVtDQojbnVtYmVyIG9mIGNsdXN0ZXJzDQpmdml6X25iY2x1c3Qobm9ybWFsaXplZF9jbHVzdGVyWywyOjRdLCBrbWVhbnMsIG1ldGhvZCA9ICJ3c3MiKQ0KDQojay1tZWFucyANCiMgQ2x1c3RlciB1c2luZyBrbWVhbnMgd2l0aCBmaXZlIGNsdXN0ZXJzDQpjbHVzdGVyX3NvbHV0aW9uIDwtIGttZWFucyhub3JtYWxpemVkX2NsdXN0ZXJbLDI6NF0sIGNlbnRlcnMgPSAyKQ0KDQojIFN0b3JlIHRoZSBjbHVzdGVyIGFzc2lnbm1lbnRzIGJhY2sgaW50byB0aGUgY2x1c3RlcmluZyBkYXRhIGZyYW1lIG9iamVjdA0KY2x1c3RlcmVkX3N1YnN1cmZhY2UkY2x1c3RlciA8LWZhY3RvcihjbHVzdGVyX3NvbHV0aW9uJGNsdXN0ZXIpIA0KDQojIExvb2sgYXQgdGhlIGRpc3RyaWJ1dGlvbiBvZiBjbHVzdGVyIGFzc2lnbm1lbnRzDQp0YWJsZShjbHVzdGVyZWRfc3Vic3VyZmFjZSRjbHVzdGVyKQ0KDQojIEdyb3VwIGJ5IHRoZSBjbHVzdGVyIGFzc2lnbm1lbnQgYW5kIGNhbGN1bGF0ZSBhdmVyYWdlcw0Kc3lzX2NsdXNfYXZnIDwtIGNsdXN0ZXJlZF9zdWJzdXJmYWNlICU+JQ0KICAgIGdyb3VwX2J5KGNsdXN0ZXIpICU+JQ0KICAgIHN1bW1hcml6ZV9pZihpcy5udW1lcmljLCBtZWFuKQ0KDQojIFZpZXcgdGhlIHJlc3VsdGluZyB0YWJsZQ0Ka2FibGUoc3lzX2NsdXNfYXZnKQ0KDQojMyBEIHBsb3QNCnBsb3RfbHkoY2x1c3RlcmVkX3N1YnN1cmZhY2UsIHggPSB+c3lzX2ltcGVydmRhX2Z0MiwgeSA9IH5zeXNfcmF3c3Rvcm1zaXplbWFuYWdlZF9pbiwgej0gfmxvYWRpbmdfcmF0aW8pICU+JQ0KICBhZGRfbWFya2Vycyhjb2xvciA9IH5jbHVzdGVyKQ0KYGBgDQoNCioqLVN1YnN1cmZhY2Ugc2xvdyByZWxlYXNlIChsaW5lZCkgY2x1c3RlcmluZzoqKg0KDQpgYGB7ciBzZWN0aW9uIDE2IFN1YnN1cmZhY2Ugc2xvdyByZWxlYXNlIChsaW5lZCkJIGNsdXN0ZXJpbmd9DQoNCmNsdXN0ZXJlZF9zbG93cmVsX2xpbmVkIDwtIHRhYmxlX2FsbCAlPiUNCiAgZmlsdGVyKHN5c19tb2RlbGlucHV0Y2F0ZWdvcnkgPT0gIlN1YnN1cmZhY2Ugc2xvdyByZWxlYXNlIChsaW5lZCkiKSAlPiUNCiAgc2VsZWN0KHN5c3RlbV9pZCwgc3lzX2ltcGVydmRhX2Z0Miwgc3lzX3Jhd3N0b3Jtc2l6ZW1hbmFnZWRfaW4sIHN5c19tb2RlbGlucHV0Y2F0ZWdvcnkpICU+JQ0KICBuYS5vbWl0KCkgJT4lDQogIGRpc3RpbmN0KCkNCg0KI25vcm1hbHplIHVzaW5nIG1lYW4tc2Qgc3RhbmRhcmRpemF0aW9uDQpub3JtYWxpemVkX2NsdXN0ZXIgPC0gY2x1c3RlcmVkX3Nsb3dyZWxfbGluZWQNCm5vcm1hbGl6ZWRfY2x1c3RlclssMjozXSA8LSBzY2FsZShjbHVzdGVyZWRfc2xvd3JlbF9saW5lZFssMjozXSkNCg0KI0VsYm93IG1ldGhvZCBzaG93cyBhIHN1YnRsZSBlbGJvdyBhdCBjbHVzdGVyIDQtaGVuY2UgNCBjbHVzdGVycyBpcyBvcHRpbXVtDQojbnVtYmVyIG9mIGNsdXN0ZXJzDQpmdml6X25iY2x1c3Qobm9ybWFsaXplZF9jbHVzdGVyWywyOjNdLCBrbWVhbnMsIG1ldGhvZCA9ICJ3c3MiKQ0KDQojay1tZWFucyANCiMgQ2x1c3RlciB1c2luZyBrbWVhbnMgd2l0aCBmaXZlIGNsdXN0ZXJzDQpjbHVzdGVyX3NvbHV0aW9uIDwtIGttZWFucyhub3JtYWxpemVkX2NsdXN0ZXJbLDI6M10sIGNlbnRlcnMgPSAyKQ0KDQojIFN0b3JlIHRoZSBjbHVzdGVyIGFzc2lnbm1lbnRzIGJhY2sgaW50byB0aGUgY2x1c3RlcmluZyBkYXRhIGZyYW1lIG9iamVjdA0KY2x1c3RlcmVkX3Nsb3dyZWxfbGluZWQkY2x1c3RlciA8LWZhY3RvcihjbHVzdGVyX3NvbHV0aW9uJGNsdXN0ZXIpIA0KDQojIExvb2sgYXQgdGhlIGRpc3RyaWJ1dGlvbiBvZiBjbHVzdGVyIGFzc2lnbm1lbnRzDQp0YWJsZShjbHVzdGVyZWRfc2xvd3JlbF9saW5lZCRjbHVzdGVyKQ0KDQojIEdyb3VwIGJ5IHRoZSBjbHVzdGVyIGFzc2lnbm1lbnQgYW5kIGNhbGN1bGF0ZSBhdmVyYWdlcw0Kc3lzX2NsdXNfYXZnIDwtIGNsdXN0ZXJlZF9zbG93cmVsX2xpbmVkICU+JQ0KICAgIGdyb3VwX2J5KGNsdXN0ZXIpICU+JQ0KICAgIHN1bW1hcml6ZV9pZihpcy5udW1lcmljLCBtZWFuKQ0KDQojIFZpZXcgdGhlIHJlc3VsdGluZyB0YWJsZQ0Ka2FibGUoc3lzX2NsdXNfYXZnKQ0KDQojMyBEIHBsb3QNCnBsb3RfbHkoY2x1c3RlcmVkX3Nsb3dyZWxfbGluZWQsIHggPSB+c3lzX2ltcGVydmRhX2Z0MiwgeSA9IH5zeXNfcmF3c3Rvcm1zaXplbWFuYWdlZF9pbikgJT4lDQogIGFkZF9tYXJrZXJzKGNvbG9yID0gfmNsdXN0ZXIpDQoNCg0KYGBgDQoNCioqLVN1YnN1cmZhY2Ugc2xvdyByZWxlYXNlICh1bmxpbmVkKSBjbHVzdGVyaW5nOioqDQoNCmBgYHtyIHNlY3Rpb24gMTcgU3Vic3VyZmFjZSBzbG93IHJlbGVhc2UgKHVubGluZWQpCX0NCg0KY2x1c3RlcmVkX3Nsb3dyZWxfdW5saW5lZCA8LSB0YWJsZV9hbGwgJT4lDQogIGZpbHRlcihzeXNfbW9kZWxpbnB1dGNhdGVnb3J5ID09ICJTdWJzdXJmYWNlIHNsb3cgcmVsZWFzZSAodW5saW5lZCkiKSAlPiUNCiAgc2VsZWN0KHN5c3RlbV9pZCwgc3lzX2ltcGVydmRhX2Z0Miwgc3lzX3Jhd3N0b3Jtc2l6ZW1hbmFnZWRfaW4sIGxvYWRpbmdfcmF0aW8sIHN5c19tb2RlbGlucHV0Y2F0ZWdvcnkpICU+JQ0KICBuYS5vbWl0KCkgJT4lDQogIGRpc3RpbmN0KCkNCg0KI25vcm1hbHplIHVzaW5nIG1lYW4tc2Qgc3RhbmRhcmRpemF0aW9uDQpub3JtYWxpemVkX2NsdXN0ZXIgPC0gY2x1c3RlcmVkX3Nsb3dyZWxfdW5saW5lZA0Kbm9ybWFsaXplZF9jbHVzdGVyWywyOjNdIDwtIHNjYWxlKGNsdXN0ZXJlZF9zbG93cmVsX3VubGluZWRbLDI6M10pDQoNCiNFbGJvdyBtZXRob2Qgc2hvd3MgYSBzdWJ0bGUgZWxib3cgYXQgY2x1c3RlciA0LWhlbmNlIDQgY2x1c3RlcnMgaXMgb3B0aW11bQ0KI251bWJlciBvZiBjbHVzdGVycw0KZnZpel9uYmNsdXN0KG5vcm1hbGl6ZWRfY2x1c3RlclssMjozXSwga21lYW5zLCBtZXRob2QgPSAid3NzIikNCg0KI2stbWVhbnMgDQojIENsdXN0ZXIgdXNpbmcga21lYW5zIHdpdGggZml2ZSBjbHVzdGVycw0KY2x1c3Rlcl9zb2x1dGlvbiA8LSBrbWVhbnMobm9ybWFsaXplZF9jbHVzdGVyWywyOjNdLCBjZW50ZXJzID0gMikNCg0KIyBTdG9yZSB0aGUgY2x1c3RlciBhc3NpZ25tZW50cyBiYWNrIGludG8gdGhlIGNsdXN0ZXJpbmcgZGF0YSBmcmFtZSBvYmplY3QNCmNsdXN0ZXJlZF9zbG93cmVsX3VubGluZWQkY2x1c3RlciA8LWZhY3RvcihjbHVzdGVyX3NvbHV0aW9uJGNsdXN0ZXIpIA0KDQojIExvb2sgYXQgdGhlIGRpc3RyaWJ1dGlvbiBvZiBjbHVzdGVyIGFzc2lnbm1lbnRzDQp0YWJsZShjbHVzdGVyZWRfc2xvd3JlbF91bmxpbmVkJGNsdXN0ZXIpDQoNCiMgR3JvdXAgYnkgdGhlIGNsdXN0ZXIgYXNzaWdubWVudCBhbmQgY2FsY3VsYXRlIGF2ZXJhZ2VzDQpzeXNfY2x1c19hdmcgPC0gY2x1c3RlcmVkX3Nsb3dyZWxfdW5saW5lZCAlPiUNCiAgICBncm91cF9ieShjbHVzdGVyKSAlPiUNCiAgICBzdW1tYXJpemVfaWYoaXMubnVtZXJpYywgbWVhbikNCg0KIyBWaWV3IHRoZSByZXN1bHRpbmcgdGFibGUNCmthYmxlKHN5c19jbHVzX2F2ZykNCg0KIzMgRCBwbG90DQpwbG90X2x5KGNsdXN0ZXJlZF9zbG93cmVsX3VubGluZWQsIHggPSB+c3lzX2ltcGVydmRhX2Z0MiwgeSA9IH5zeXNfcmF3c3Rvcm1zaXplbWFuYWdlZF9pbiwgeiA9IH5sb2FkaW5nX3JhdGlvKSAlPiUNCiAgYWRkX21hcmtlcnMoY29sb3IgPSB+Y2x1c3RlcikNCmBgYA0KDQpVcG9uIGNsdXN0ZXJpbmcgdGhlIHN5c3RlbXMsIHRoZSBkYXRhIHdpbGwgYmUgY29sbGVjdGVkIGluIGEgdW5pZmllZCBkYXRhIGZyYW1lIGZvciBzdHJhdGlmaWVkIHNhbXBsaW5nLiBUaGUgc3RyYXRpZmllZCBzYW1wbGluZyBjcmVhdGVzIGEgYmFsYW5jZWQgbGlzdCBvZiBzeXN0ZW1zIHRoYXQgYXJlIHJlZmxlY3RpdmUgb2YgdGhlIHNpemUgb2YgZWFjaCBzeXN0ZW0gdHlwZSBhbmQgdGhhdCBvZiB0aGUgaW50ZXJuYWwgY2x1c3RlcnMuIEZvciBleGFtcGxlLCB0aGUgY29kZSBiZWxvdyBoYXMgZ2VuZXJhdGVkIDU4IHN5c3RlbSAoXH4gMTAlIG9mIHRvdGFsIHVubW9uaXRvcmVkIHN5c3RlbXMpOyB0aGUgc2l6ZSBvZiBlYWNoIHN5c3RlbSB0eXBlIGlzIHByb3BvcnRpb25hbCB0byB0aGF0IG9mIHRoZSBwb3B1bGF0aW9uLCBhbmQgd2l0aGluIGVhY2ggdHlwZSBvZiBzeXN0ZW1zLCBhIHByb3BvcnRpb25hbCBudW1iZXIgb2Ygc3lzdGVtcyBoYXMgYmVlbiBjaG9zZW4gZnJvbSBlYWNoIGNsdXN0ZXIgZS5nLiwgaW4gQmlvaW5maWx0cmF0aW9uIHR5cGUgc3lzdGVtcywgNyBzeXN0ZW1zIGFyZSByYW5kb21seSBjaG9zZW4gZnJvbSBjbHVzdGVyIDMsIDMgc3lzdGVtcyBmcm9tIGNsdXN0ZXIgMiwgYW5kIDIgc3lzdGVtcyBmcm9tIGNsdXN0ZXIgMS4NCg0KYGBge3Igc2VjdGlvbiAxOCBzYW1wbGluZ30NCiNiaW5kaW5nIGFsbCBzdWItdHlwZSBncm91cHMgaW50byBvbmUgdGFibGUNCnN5c19jbHVzdGVyZWRfYWxsIDwtIGJpbmRfcm93cyhjbHVzdGVyZWRfQmlvaW5maWx0cmF0aW9uWyxjKCJzeXN0ZW1faWQiLCJjbHVzdGVyIiwic3lzX21vZGVsaW5wdXRjYXRlZ29yeSIpXSwgY2x1c3RlcmVkX0Jpb3JldGVuX2xpbmVkWyxjKCJzeXN0ZW1faWQiLCJjbHVzdGVyIiwic3lzX21vZGVsaW5wdXRjYXRlZ29yeSIpXSwgY2x1c3RlcmVkX0Jpb3JldGVuX3VubGluZWRbLGMoInN5c3RlbV9pZCIsImNsdXN0ZXIiLCJzeXNfbW9kZWxpbnB1dGNhdGVnb3J5IildLCBjbHVzdGVyZWRfc2xvd3JlbF9saW5lZFssYygic3lzdGVtX2lkIiwiY2x1c3RlciIsInN5c19tb2RlbGlucHV0Y2F0ZWdvcnkiKV0sIGNsdXN0ZXJlZF9zbG93cmVsX3VubGluZWRbLGMoInN5c3RlbV9pZCIsImNsdXN0ZXIiLCJzeXNfbW9kZWxpbnB1dGNhdGVnb3J5IildLCBjbHVzdGVyZWRfc3Vic3VyZmFjZVssYygic3lzdGVtX2lkIiwiY2x1c3RlciIsInN5c19tb2RlbGlucHV0Y2F0ZWdvcnkiKV0pDQoNCiNzYW1wbGluZyBjb2RlDQpzdHJhdGlmaWVkX3NhbXBsZSA8LSBzeXNfY2x1c3RlcmVkX2FsbCAlPiUNCiAgICBncm91cF9ieShzeXNfbW9kZWxpbnB1dGNhdGVnb3J5LCBjbHVzdGVyKSAlPiUNCiAgICBzYW1wbGVfZnJhYyhzaXplPTAuMSkNCg0Ka2FibGUoc3RyYXRpZmllZF9zYW1wbGUpDQpgYGANCg0KVG8gZ2VuZXJhdGUgbW9uaXRvcmluZyBzeXN0ZW1zLCB3ZSBkZXZlbG9wZWQgYSBzZW1pLXJhbmRvbSBhcHByb2FjaCB0aGF0IHV0aWxpemVzIGEgYmlubmluZyB0ZWNobmlxdWUuIFRoaXMgYXBwcm9hY2ggaW52b2x2ZXMgYXNzaWduaW5nIHN5c3RlbXMgdG8gY2x1c3RlcnMgYmFzZWQgb24gdGhlaXIgc3BlY2lmaWMgdHlwZSwgd2l0aCB0aGUgYmlubmluZyBwcm9jZXNzIHV0aWxpemluZyBhIGNvbWJpbmF0aW9uIG9mIGRlc2lnbiBtZXRyaWNzIHRvIGdyb3VwIHNpbWlsYXIgc3lzdGVtcyB0b2dldGhlci4gVG8gZW5zdXJlIGEgcmVwcmVzZW50YXRpdmUgc2FtcGxlIG9mIHN5c3RlbXMsIHRoZSByYW5kb20gc2VsZWN0aW9uIHByb2Nlc3MgY2hvb3NlcyBhIHByb3BvcnRpb25hbCBudW1iZXIgb2Ygc3lzdGVtcyBmcm9tIGVhY2ggY2x1c3RlciBhbmQgc3lzdGVtIHR5cGUgYmFzZWQgb24gdGhlaXIgc2l6ZS4NCg==